
/**
 * nobittermodel.cpp
 * 
 * NobitterModel is the model class to hold all Tweet
 * 
 * Copyright (c) 2008 Harunobu Oyama <nobukichi at gmail.com>
 * Licensed under GNU GPL version 3
 *
 */

#ifdef WIN32
#pragma warning( disable:4786 )
#endif

#include	<assert.h>
#include	<QCoreApplication>
#include	<QTextStream>
#include	<QIcon>
#include	"concretefilters.h"
#include	"imagecache.h"
#include	"nobittermodel.h"

NobitterModel::NobitterModel(QObject * parent)
  : QStandardItemModel(0, 5, parent), 
    pWhoFilter(ShowAllFilter::getInstance()), 
    pAtFilter(ShowAllFilter::getInstance()), 
	pWhenFilter(ShowAllFilter::getInstance())
{
    setHeaderData(0, Qt::Horizontal, tr("Date"));
	setHeaderData(1, Qt::Horizontal, tr("Time"));
	setHeaderData(2, Qt::Horizontal, tr("Name"));
	setHeaderData(3, Qt::Horizontal, "@");
	setHeaderData(4, Qt::Horizontal, tr("Twit"));

	updateModelTimer.setSingleShot(true);
	updateModelTimer.setInterval(1000);
	connect( &updateModelTimer, SIGNAL(timeout()),
		     this, SLOT(slotUpdateModel()) );
	connect( ImageCache::getInstance(), SIGNAL(signalImageDownloaded()),
	         this, SLOT(slotStartUpdateModelTimer()) );
}
	
void NobitterModel::feedTweets(const TweetVector& tweets)
{
	//! remove local tweet cache from tweetsArchive
	TweetSet::iterator it;
	for (it = myLocalTweet.begin(); it != myLocalTweet.end(); ++it) {
		tweetsArchive.erase(*it);
	}
	myLocalTweet.clear();

	for (unsigned int i = 0; i < tweets.size(); ++i) {
		tweetsArchive.insert(tweets[i]);
	}
	updateModelTimer.stop();
	updateModelTimer.start();
}

void NobitterModel::slotStartUpdateModelTimer()
{
	updateModelTimer.stop();
	updateModelTimer.start();
}

Tweet NobitterModel::tweetNewerThan(const Tweet& baseTweet)
{
	TweetSet::reverse_iterator it;
	for (it = tweetsArchive.rbegin(); it != tweetsArchive.rend(); ++it) {
		if ((myLocalTweet.find(*it) == myLocalTweet.end()) && (*it < baseTweet)) {
			return *it;
		}
	}
	return Tweet();
}

void NobitterModel::feedMyTweet(const Tweet& myTweet)
{
	myLocalTweet.insert(myTweet);
	tweetsArchive.insert(myTweet);
	slotUpdateModel();
}

void NobitterModel::setWhoFilter(Filter* filter)
{
	if (filter == 0) {
		pWhoFilter = ShowAllFilter::getInstance();
	} else {
		pWhoFilter = filter;
	}
	slotUpdateModel();
}

void NobitterModel::setAtFilter(Filter* filter)
{
	if (filter == 0) {
		pAtFilter = ShowAllFilter::getInstance();
	} else {
		pAtFilter = filter;
	}
	slotUpdateModel();
}

void NobitterModel::setWhenFilter(Filter* filter)
{
	if (filter == 0) {
		pWhenFilter = ShowAllFilter::getInstance();
	} else {
		pWhenFilter = filter;
	}
	slotUpdateModel();
}

void NobitterModel::slotUpdateModel()
{
	if (pWhoFilter == 0 || pAtFilter == 0 || pWhenFilter == 0) {
#ifdef _DEBUG
		assert(false);
#endif
		return;
	}

	pWhoFilter->init();
	pWhenFilter->init();
	pAtFilter->init();

	removeRows(0, rowCount());

	TweetSet::iterator it;
	for (it = tweetsArchive.begin(); it != tweetsArchive.end(); ++it) {
		int row = rowCount();
		if (pWhoFilter->test(*it) && pAtFilter->test(*it) && pWhenFilter->test(*it)) {
			insertRow(row);
			setData(index(row, 0), it->timeStamp().date());
			setData(index(row, 1), it->timeStamp().time());

			setData(index(row, 2), ImageCache::getInstance()->icon(it->userName(), it->imageUrl()), Qt::DecorationRole);

			setData(index(row, 2), it->userName());
			setData(index(row, 3), it->atUser());
			setData(index(row, 4), it->tweetText());
		}
	}

	emit signalModelUpdated();
}
	
void NobitterModel::init()
{
	myLocalTweet.clear();
	tweetsArchive.clear();
	removeRows(0, rowCount());
}

bool NobitterModel::save(QTextStream& out)
{
	bool succeeded = true;

	TweetSet::iterator it;
	for (it = tweetsArchive.begin(); it != tweetsArchive.end(); ++it) {
		if (myLocalTweet.find(*it) == myLocalTweet.end()) {
			out << it->toString() << endl;
		}
	}

	return succeeded;
}


void NobitterModel::saveLogInMonth(int year, int month, QTextStream& out)
{
	TweetSet::iterator it;
	for (it = tweetsArchive.begin(); it != tweetsArchive.end(); ++it) {
		if (it->timeStamp().date().year() == year && it->timeStamp().date().month() == month) {
			out << it->toString() << endl;
		}
	}
}

void NobitterModel::removeTweetsBefore(const QDate& dateBefore)
{
	TweetSet tmp;

	TweetSet::iterator it;
	for (it = tweetsArchive.begin(); it != tweetsArchive.end(); ++it) {
		if (dateBefore <= it->timeStamp().date()) {
			tmp.insert(*it);
		}
	}

	tweetsArchive.swap(tmp);
}

bool NobitterModel::load(QTextStream& in)
{
	bool succeeded = true;

	while (!in.atEnd()) {
		QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
		QString line = in.readLine();
		Tweet tweet = Tweet::fromString(line);
		if (!tweet.userName().isEmpty()) {
			tweetsArchive.insert(tweet);
		}
	}

	return succeeded;
}

QDateTime NobitterModel::mostRecentDateTime()
{
	TweetSet::iterator it;
	for (it = tweetsArchive.begin(); it != tweetsArchive.end(); ++it) {
		//! we want to skip the own post which is locally cached,
		//! because we want to fill the gap between what we have already downloaded and
		//! what we are currently downloading
		if (myLocalTweet.find(*it) == myLocalTweet.end()) {
			return it->timeStamp();
		}
	}

	return QDateTime(QDate(2000,1,1)); //! old enough time
}

std::set<QString> NobitterModel::getUserNames()
{
	std::set<QString> userNames;
	
	TweetSet::iterator it;
	for (it = tweetsArchive.begin(); it != tweetsArchive.end(); ++it) {
		userNames.insert(it->userName());
		if (!it->atUser().isEmpty()) {
			userNames.insert(it->atUser());
		}
	}
	
	return userNames;
}


